home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ms_sh21s.zip / SH210 / SRC / SH4.C < prev    next >
C/C++ Source or Header  |  1992-12-14  |  39KB  |  1,835 lines

  1. /* MS-DOS SHELL - 'word' Interpretator
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990,1,2 Data Logic Limited and Charles Forsyth
  4.  *
  5.  * This code is based on (in part) the shell program written by Charles
  6.  * Forsyth and is subject to the following copyright restrictions:
  7.  *
  8.  * 1.  Redistribution and use in source and binary forms are permitted
  9.  *     provided that the above copyright notice is duplicated in the
  10.  *     source form and the copyright notice in file sh6.c is displayed
  11.  *     on entry to the program.
  12.  *
  13.  * 2.  The sources (or parts thereof) or objects generated from the sources
  14.  *     (or parts of sources) cannot be sold under any circumstances.
  15.  *
  16.  *    $Header: /usr/users/istewart/src/shell/sh2.1/RCS/sh4.c,v 2.4 1992/12/14 10:54:56 istewart Exp $
  17.  *
  18.  *    $Log: sh4.c,v $
  19.  *    Revision 2.4  1992/12/14  10:54:56  istewart
  20.  *    BETA 215 Fixes and 2.1 Release
  21.  *
  22.  *    Revision 2.3  1992/11/06  10:03:44  istewart
  23.  *    214 Beta test updates
  24.  *
  25.  *    Revision 2.2  1992/09/03  18:54:45  istewart
  26.  *    Beta 213 Updates
  27.  *
  28.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  29.  *    211 Beta updates
  30.  *
  31.  *    Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
  32.  *    MS-Shell 2.0 Baseline release
  33.  *
  34.  */
  35.  
  36. #include <sys/types.h>
  37. #include <sys/stat.h>
  38. #include <stdio.h>
  39. #include <limits.h>            /* String library functions     */
  40. #include <signal.h>
  41. #include <errno.h>
  42. #include <setjmp.h>
  43. #include <dirent.h>
  44. #include <string.h>
  45. #include <stdlib.h>
  46. #include <unistd.h>
  47. #include <ctype.h>
  48. #ifdef OS2
  49. #define INCL_DOSSESMGR
  50. #define INCL_DOSDEVICES
  51. #include <os2.h>            /* OS2 functions declarations       */
  52. #else
  53. #include <dos.h>
  54. #include <bios.h>            /* DOS BIOS functions        */
  55. #endif
  56. #include <malloc.h>            /* Malloc functions        */
  57. #include <glob.h>
  58.  
  59. /* Special Addition to glob.h flags for the shell */
  60.  
  61. #define GLOB_CONVERT    0x0100        /* Convert file names        */
  62.  
  63. #include "sh.h"
  64.  
  65. /*
  66.  * ${}, `command`, blank interpretation, quoting and file name expansion
  67.  */
  68.  
  69. #define    NSTART    16        /* default number of words to    */
  70.                 /* allow for initially        */
  71. static char    *spcl  = "[?*";
  72. static char    *_GP_MetaChars = "?*[\\";
  73. static char    *spcl1 = "\"'";
  74. static char    *bad_subs = "bad substitution\n";
  75. static bool    GlobbingInterrupted = FALSE;
  76.  
  77. /*
  78.  * Global variables for TWALK to build command environment
  79.  */
  80.  
  81. static Word_B    *BCE_WordList;
  82. static int    BCE_Length;
  83.  
  84. static bool near    ExpandNames (char *, Word_B **, int);
  85. static char near    ExpandEnvironmentVariables (bool, int);
  86. static bool near    ExpandGravedCommand (bool, char);
  87. static Word_B * near    ExpandGlobbedName (char *, Word_B *, int);
  88. static char * near    blank (int);
  89. static char * near    unquote (char *, bool);
  90. static Word_B * near    CreateNewWordBlock (int);
  91. static bool near    CheckForReDirect (C_Op *, int);
  92. static bool near    ProcessVariable (char, bool);
  93. static void        BuildEnvironmentEntry (void *, VISIT, int);
  94.  
  95. static int near        _GP_ExpandField    (char *, char *, glob_t *);
  96. static int near        _GP_ExpandMetaCharacters (char *, glob_t *);
  97. static int near        _GP_StdargvAddArgument (char *, glob_t *);
  98. static int near        _GP_ShellAddArgument (char *, glob_t *);
  99. static char * near    _GP_CheckForMultipleDrives (char *);
  100. static void         _GP_HandleInterrupt (int);
  101. static int near        _GP_GetNumberofFloppyDrives (void);
  102.  
  103. static int (near * _GP_AddArgument) (char *, glob_t *) = _GP_StdargvAddArgument;
  104.  
  105. /*
  106.  * Expand all words to their full potential
  107.  */
  108.  
  109. char **eval (register char **NameList, int ExpandMode,
  110.          struct ExecutableProcessing *PMode)
  111. {
  112.     Word_B    *wb = (Word_B *)NULL;
  113.     char    **wp = (char **)NULL;
  114.     char    **wf = (char **)NULL;
  115.     jmp_buf    ev;
  116.     bool    DecideProg = TRUE;
  117.     int        Start;
  118.  
  119. /* Create a new IO environment */
  120.  
  121.     if (CreateNewEnvironment (setjmp (ErrorReturnPoint = ev)) == FALSE)
  122.     {
  123.     while ((*NameList != (char *)NULL) && IsVariableAssignment (*NameList))
  124.         ExpandNames (*(NameList++), &wb, ExpandMode & ~EXPAND_GLOBBING);
  125.  
  126.     if (FL_TEST ('k'))
  127.     {
  128.         for (wf = NameList; *wf != (char *)NULL; wf++)
  129.         {
  130.         if (IsVariableAssignment (*wf))
  131.             ExpandNames (*wf, &wb, ExpandMode & ~EXPAND_GLOBBING);
  132.         }
  133.     }
  134.  
  135. /* Now expand the words */
  136.  
  137.     for (wb = AddWordToBlock ((char *)NULL, wb), Start = wb->w_nword;
  138.          *NameList; NameList++)
  139.     {
  140.         if (!FL_TEST ('k') || !IsVariableAssignment (*NameList))
  141.         ExpandNames (*NameList, &wb, ExpandMode & ~EXPAND_MOVE);
  142.  
  143. /* Get the program mode for expansion of words and globs */
  144.  
  145.         if ((PMode != (struct ExecutableProcessing *)NULL) && DecideProg)
  146.         {
  147.         CheckProgramMode (wb->w_words[Start], PMode);
  148.  
  149.         ExpandMode = (PMode->Flags & EP_NOEXPAND)
  150.                 ? EXPAND_ALL & ~EXPAND_GLOBBING : EXPAND_ALL;
  151.  
  152.         if (PMode->Flags & EP_NOWORDS)
  153.             ExpandMode &= ~EXPAND_SPLITIFS;
  154.  
  155.         if (PMode->Flags & EP_CONVERT)
  156.             ExpandMode |= EXPAND_CONVERT;
  157.  
  158.         DecideProg = FALSE;
  159.         }
  160.     }
  161.  
  162. /* Get the word list */
  163.  
  164.     wp = GetWordList (AddWordToBlock (NOWORD, wb));
  165.     QuitCurrentEnvironment ();
  166.     }
  167.  
  168.     else
  169.     ExpansionErrorDetected = TRUE;
  170.  
  171.     return ExpansionErrorDetected ? (char **)NULL : wp;
  172. }
  173.  
  174. /*
  175.  * Make the exported environment from the exported names in the dictionary.
  176.  * Keyword assignments will already have been done.  Convert to MSDOS
  177.  * format if flag set and m enabled
  178.  */
  179.  
  180.  
  181. char **BuildCommandEnvironment (void)
  182. {
  183. /* Update SECONDS and RANDOM */
  184.  
  185.     HandleSECONDandRANDOM ();
  186.  
  187. /* Build the environment by walking the tree */
  188.  
  189.     BCE_WordList = (Word_B *)NULL;
  190.     BCE_Length   = 0;
  191.  
  192.     twalk (VariableTree, BuildEnvironmentEntry);
  193.  
  194.     if (BCE_Length >= 0x7f00)
  195.         return (char **)NULL;
  196.  
  197.     return GetWordList (AddWordToBlock (NOWORD, BCE_WordList));
  198. }
  199.  
  200. /*
  201.  * TWALK Function - Build Export VARIABLE list from VARIABLE tree
  202.  */
  203.  
  204. static void BuildEnvironmentEntry (void *key, VISIT visit, int level)
  205. {
  206.     VariableList    *vp = *(VariableList **)key;
  207.     char        *cp;
  208.     char        *sp;
  209.     int            tlen;
  210.  
  211.     if ((visit != postorder) && (visit != leaf))
  212.     return;
  213.  
  214.     if (vp->status & STATUS_EXPORT)
  215.     {
  216.     cp = GetVariableAsString (vp->name, TRUE);
  217.     tlen = strlen (vp->name) + strlen (cp) + 2;
  218.  
  219.     if ((BCE_Length += tlen) >= 0x7f00)
  220.         return;
  221.  
  222.     strcpy ((sp = GetAllocatedSpace (tlen)), vp->name);
  223.     SetMemoryAreaNumber ((void *)sp, MemoryAreaLevel);
  224.     strcat (sp, "=");
  225.     strcat (sp, cp);
  226.  
  227.     BCE_WordList = AddWordToBlock (sp, BCE_WordList);
  228.  
  229. /* If MSDOS mode, we need to copy the variable, convert / to \ and put
  230.  * the copy in the environment list instead
  231.  */
  232.  
  233.     if ((FL_TEST ('m') || (ExecProcessingMode.Flags & EP_EXPORT)) &&
  234.         (vp->status & STATUS_CONVERT_MSDOS))
  235.     {
  236.         cp = StringCopy (BCE_WordList->w_words[BCE_WordList->w_nword - 1]);
  237.         BCE_WordList->w_words[BCE_WordList->w_nword - 1] = PATH_TO_DOS (cp);
  238.     }
  239.     }
  240. }
  241.  
  242. /*
  243.  *
  244.  */
  245.  
  246. char *evalstr (register char *cp, int ExpandMode)
  247. {
  248.     Word_B    *wb = (Word_B *)NULL;
  249.  
  250. /* Expand the name */
  251.  
  252.     if (ExpandNames (cp, &wb, ExpandMode))
  253.     {
  254.     if ((wb == (Word_B *)NULL) || (wb->w_nword == 0) ||
  255.         ((cp = wb->w_words[0]) == (char *)NULL))
  256.         cp = null;
  257.  
  258.     ReleaseMemoryCell ((void *)wb);
  259.     }
  260.  
  261.     else
  262.     cp = (char *)NULL;
  263.  
  264.     return cp;
  265. }
  266.  
  267. /* Expand special characters and variables */
  268.  
  269. static bool near ExpandNames (register char  *cp, /* String to process    */
  270.                   register Word_B **wbp,    /* Word block    */
  271.                   int ExpandMode)        /* Expand mode    */
  272. {
  273.     jmp_buf    ev;
  274.     char    *IFS_Value = GetVariableAsString (IFS, FALSE);
  275.  
  276.     ExpansionErrorDetected = FALSE;
  277.  
  278.     if (cp == (char *)NULL)
  279.     return FALSE;
  280.  
  281. /* If there are no special characters and no separators, nothing to do,
  282.  * just save the word
  283.  */
  284.  
  285.     if (!anys (spcl2, cp) && !anys (IFS_Value, cp) &&
  286.      ((ExpandMode & EXPAND_GLOBBING) == 0 || !anys (spcl, cp)))
  287.     {
  288.     cp = StringCopy (cp);
  289.  
  290.     if (ExpandMode & DOTRIM)
  291.         unquote (cp, (bool)(ExpandMode & EXPAND_CONVERT));
  292.  
  293.     *wbp = AddWordToBlock (cp, *wbp);
  294.     return TRUE;
  295.     }
  296.  
  297. /* Set up to read the word back in */
  298.  
  299.     if (CreateNewEnvironment (setjmp (ErrorReturnPoint = ev)) == FALSE)
  300.     {
  301.     PUSHIO (aword, cp, String_GetNextCharacter, null);
  302.     e.iobase = e.iop;
  303.  
  304.     while ((cp = blank (ExpandMode)) && !ExpansionErrorDetected)
  305.     {
  306.         e.linep = cp;
  307.         cp = StringCopy (cp);
  308.  
  309. /* Global expansion disabled ? */
  310.  
  311.         if (((ExpandMode & EXPAND_GLOBBING) == 0) || FL_TEST ('f'))
  312.         {
  313.         if (ExpandMode & DOTRIM)
  314.             unquote (cp, (bool)(ExpandMode & EXPAND_CONVERT));
  315.  
  316.         *wbp = AddWordToBlock (cp, *wbp);
  317.         }
  318.  
  319.         else
  320.         *wbp = ExpandGlobbedName (cp, *wbp,
  321.             (ExpandMode & EXPAND_CONVERT)
  322.                 ? (GLOB_NOCHECK | GLOB_CONVERT) : GLOB_NOCHECK);
  323.     }
  324.  
  325.     QuitCurrentEnvironment ();
  326.     }
  327.  
  328.     else
  329.     ExpansionErrorDetected = TRUE;
  330.  
  331.     return !ExpansionErrorDetected ? TRUE : FALSE;
  332. }
  333.  
  334. /*
  335.  * Blank interpretation and quoting
  336.  */
  337.  
  338. static char * near blank (int ExpandMode)
  339. {
  340.     register int    c, c1;
  341.     register char    *sp = e.linep;
  342.     bool        scanequals = (ExpandMode & EXPAND_MOVE) ? TRUE : FALSE;
  343.     bool        foundequals = FALSE;
  344.     char        *IFS_Value = GetVariableAsString (IFS, FALSE);
  345.  
  346. loop:
  347.     switch (c = subgetc (CHAR_DOUBLE_QUOTE, foundequals))
  348.     {
  349.     case 0:
  350.         if (sp == e.linep)
  351.         return (char *)NULL;
  352.  
  353.         *(e.linep++) = 0;
  354.         return sp;
  355.  
  356.     default:
  357.         if ((ExpandMode & EXPAND_SPLITIFS) && any ((char)c, IFS_Value))
  358.         goto loop;
  359.  
  360.         break;
  361.  
  362.     case CHAR_DOUBLE_QUOTE:
  363.     case CHAR_SINGLE_QUOTE:
  364.         scanequals = FALSE;
  365.         if (INSUB())
  366.         break;
  367.  
  368.         for (c1 = c; (c = subgetc ((char)c1, TRUE)) != c1;)
  369.         {
  370.         if (c == 0)
  371.             break;
  372.  
  373.         if ((c == CHAR_SINGLE_QUOTE) || !any ((char)c, "$`\""))
  374.             c |= QUOTE;
  375.  
  376.         *(e.linep++) = (char)c;
  377.         }
  378.  
  379.         c = 0;
  380.     }
  381.  
  382.     ReturnGotCharacter (c);
  383.  
  384.     if (!isalpha (c))
  385.     scanequals = FALSE;
  386.  
  387.     while (1)
  388.     {
  389.     if (((c = subgetc (CHAR_DOUBLE_QUOTE, foundequals)) == 0) ||
  390.         (ExpandMode & EXPAND_SPLITIFS) &&
  391.          any ((char)c, IFS_Value) || !INSUB() && any ((char)c, spcl1))
  392.     {
  393.         scanequals = FALSE;
  394.         ReturnGotCharacter (c);
  395.  
  396.         if (any ((char)c, spcl1))
  397.         goto loop;
  398.  
  399.         break;
  400.     }
  401.  
  402.     if (scanequals)
  403.     {
  404.         if (c == '=')
  405.         {
  406.         foundequals = TRUE;
  407.         scanequals  = FALSE;
  408.         }
  409.  
  410.         else if (!isalnum (c))
  411.         scanequals = FALSE;
  412.     }
  413.  
  414.     *(e.linep++) = (char)c;
  415.     }
  416.  
  417.     *(e.linep++) = 0;
  418.     return sp;
  419. }
  420.  
  421. /*
  422.  * Get characters, substituting for ` and $
  423.  */
  424.  
  425. int  subgetc (register char ec, bool quoted)
  426. {
  427.     register char    c;
  428.  
  429.     while (1)
  430.     {
  431.     c = (char)GetNextCharacter (ec);
  432.  
  433.     if (!INSUB() && ec != CHAR_SINGLE_QUOTE)
  434.     {
  435.  
  436. /* Found a ` - execute the command */
  437.  
  438.         if (c == CHAR_BACKQUOTE)
  439.         {
  440.  
  441. /* If both ec (end character) is zero and quoted flag is FALSE, this is execute
  442.  * command request is in a here document, so we have to collect the rest of
  443.  * the command from input.  Otherwise, the command is in e.iop->argp->aword.
  444.  *
  445.  * We also need to set quoted so that CHAR_NEW_LINE are not processed when
  446.  * reading the output from the command.
  447.  */
  448.         if (!ec && !quoted)
  449.         {
  450.             e.linep = e.cline;
  451.             if (CollectInputToCharacter (c, c) != 0)
  452.             return 0;
  453.  
  454.             e.iop->argp->aword = e.cline + 1;
  455.             quoted = MAYBE;
  456.         }
  457.  
  458.         if (ExpandGravedCommand (quoted, CHAR_BACKQUOTE) == 0)
  459.             return 0;
  460.  
  461. /* Re-read the character from the Grave function */
  462.  
  463.         e.iop->TaskType = X_FROM_STDOUT;
  464.         }
  465.  
  466. /* $ - check for environment variable subsitution */
  467.  
  468.         else if (c == '$')
  469.         {
  470.         if (!ProcessVariable (ec, quoted))
  471.             return 0;
  472.         }
  473.  
  474. /* No special processing required - return the character */
  475.  
  476.         else
  477.         return c;
  478.     }
  479.  
  480.     else
  481.         return c;
  482.     }
  483. }
  484.  
  485. /*
  486.  * Handle $() in ${}
  487.  */
  488.  
  489. static bool near ProcessVariable (char ec, bool quoted)
  490. {
  491.     int        c;
  492.     int        passed = 0;
  493.  
  494.     if ((c = GetNextCharacter (0)) == CHAR_OPEN_PARATHENSIS)
  495.     {
  496.     if ((passed = GetNextCharacter (0)) != CHAR_OPEN_PARATHENSIS)
  497.     {
  498.  
  499. /* If both ec (end character) is zero and quoted flag is FALSE, this is execute
  500.  * command request is in a here document, so we have to collect the rest of
  501.  * the command from input.  Otherwise, the command is in e.iop->argp->aword.
  502.  *
  503.  * We also need to set quoted so that CHAR_NEW_LINE are not processed when
  504.  * reading the output from the command.
  505.  */
  506.  
  507.         if (!ec && !quoted)
  508.         {
  509.         e.linep = e.cline;
  510.  
  511.         if (ScanForEndofWord (passed, SWT_STDOUT))
  512.             return 0;
  513.  
  514.         e.iop->argp->aword = e.cline;
  515.         *(--e.linep) = CHAR_BACKQUOTE;
  516.         *(++e.linep) = 0;
  517.         quoted = MAYBE;
  518.         }
  519.  
  520. /* Replace the closing ) with a backquote.  Remember that we've already
  521.  * absorbed a character, so remove it
  522.  */
  523.  
  524.         else
  525.         {
  526.         --(e.iop->argp->aword);
  527.         c = strlen (e.iop->argp->aword) - 2;
  528.         if (e.iop->argp->aword [c] != CHAR_CLOSE_PARATHENSIS)
  529.             c++;
  530.  
  531.         e.iop->argp->aword [c] = CHAR_BACKQUOTE;
  532.         }
  533.  
  534.         if (ExpandGravedCommand (quoted, CHAR_CLOSE_PARATHENSIS) == 0)
  535.         return FALSE;
  536.  
  537. /* Re-read the character from the Grave function */
  538.  
  539.         e.iop->TaskType = X_FROM_STDOUT;
  540.         return TRUE;
  541.     }
  542.  
  543. /* OK - must be a $((...)) */
  544.  
  545.     c = passed;
  546.     passed = CHAR_OPEN_PARATHENSIS;
  547.     }
  548.  
  549. /* Handle environment variable */
  550.  
  551.     ReturnGotCharacter (c);
  552.  
  553.     if ((c = ExpandEnvironmentVariables (quoted, passed)) == 0)
  554.     e.iop->TaskType = X_EXPAND_DOLLAR;
  555.  
  556.     return TRUE;
  557. }
  558.  
  559. /*
  560.  * Prepare to generate the string returned by ${} substitution.
  561.  */
  562.  
  563. static char near ExpandEnvironmentVariables (bool quoted, int passed)
  564. {
  565.     IO_State        *oiop;
  566.     char        *dolp, OriginalTaskType;
  567.     register char    *s, c, *cp;
  568.     register char    EndCharacter = 0;
  569.     char        StartCharacter = 0;
  570.     bool        colon_f = FALSE;
  571.     bool        hash_f = FALSE;
  572.     char        *dol_special = "$ ";
  573.     char        DecimalString[12];
  574.  
  575.     c = (char) (passed ? passed : ReadCharacterFromIOStack ());
  576.     s = e.linep;
  577.  
  578. /* Bracketed or not ? */
  579.  
  580.     if ((c != CHAR_OPEN_BRACES) && (c != CHAR_OPEN_PARATHENSIS))
  581.     {
  582.  
  583. /* Get the string, while it is a alpha character */
  584.  
  585.     *(e.linep++) = c;
  586.  
  587.     if (isalpha (c))
  588.     {
  589.         while (((c = (char)ReadCharacterFromIOStack ()) != 0) &&
  590.            isalnum (c))
  591.         {
  592.         if (e.linep < e.eline)
  593.             *(e.linep++) = c;
  594.         }
  595.  
  596.         ReturnGotCharacter (c);
  597.     }
  598.  
  599.     c = 0;
  600.     }
  601.  
  602. /* Bracketed - special case */
  603.  
  604.     else
  605.     {
  606.     oiop = e.iop;
  607.     OriginalTaskType = e.iop->TaskType;
  608.     e.iop->TaskType = X_ANYOTHER_IO;
  609.  
  610.     StartCharacter = c;
  611.     EndCharacter = (char)((c == CHAR_OPEN_BRACES) ? CHAR_CLOSE_BRACES
  612.                               : CHAR_CLOSE_PARATHENSIS);
  613.     if ((c == CHAR_OPEN_PARATHENSIS) &&
  614.         (subgetc (CHAR_DOUBLE_QUOTE, FALSE) != CHAR_OPEN_PARATHENSIS))
  615.     {
  616.         ShellErrorMessage ("missing ( in $((...))");
  617.         ExpansionErrorDetected = TRUE;
  618.         return c;
  619.     }
  620.  
  621.     while (((c = (char)subgetc (CHAR_DOUBLE_QUOTE, FALSE)) != 0) &&
  622.            (c != CHAR_NEW_LINE))
  623.     {
  624.         if (c == EndCharacter)
  625.         {
  626.         if (EndCharacter == CHAR_CLOSE_BRACES)
  627.             break;
  628.  
  629. /* Found the end character ), check that it is followed by a second ) */
  630.  
  631.         if (((c = (char)subgetc (CHAR_DOUBLE_QUOTE, FALSE)) == 0) ||
  632.             (c == CHAR_NEW_LINE) || (c == CHAR_CLOSE_PARATHENSIS))
  633.             break;
  634.  
  635.         if (e.linep < e.eline)
  636.             *(e.linep++) = CHAR_CLOSE_PARATHENSIS;
  637.         }
  638.  
  639.         if (e.linep < e.eline)
  640.         *(e.linep++) = c;
  641.     }
  642.  
  643.     if (oiop == e.iop)
  644.         e.iop->TaskType = OriginalTaskType;
  645.  
  646. /* Check terminate correctly */
  647.  
  648.     if (c != EndCharacter)
  649.     {
  650.         ShellErrorMessage ("unclosed $%c%c's", StartCharacter, EndCharacter);
  651.         ExpansionErrorDetected = TRUE;
  652.         return c;
  653.     }
  654.  
  655. /* Check for Hash at start */
  656.  
  657.     if ((*s == '#') && (e.linep - s) > 1)
  658.     {
  659.         hash_f = TRUE;
  660.         memcpy (s, s + 1, e.linep - s - 1);
  661.         --e.linep;
  662.     }
  663.  
  664. /* Check for zero length string */
  665.  
  666.     if (s == e.linep)
  667.     {
  668.         ShellErrorMessage (bad_subs);
  669.         ExpansionErrorDetected = TRUE;
  670.         return c;
  671.     }
  672.     }
  673.  
  674. /* Check line length */
  675.  
  676.     if (e.linep >= e.eline)
  677.     {
  678.     ShellErrorMessage ("string in ${} too long");
  679.     ExpansionErrorDetected = TRUE;
  680.     e.linep -= 10;
  681.     }
  682.  
  683.     *e.linep = 0;
  684.  
  685. /* Maths? */
  686.  
  687.     if (EndCharacter == CHAR_CLOSE_PARATHENSIS)
  688.     {
  689.     sprintf (DecimalString, "%lu", EvaluateMathsExpression (s));
  690.     dolp = StringCopy (DecimalString);
  691.     e.linep = s;
  692.     PUSHIO (aword, dolp, quoted ? QuotedString_GetNextCharacter
  693.                     : String_GetNextCharacter, null);
  694.     return 0;
  695.     }
  696.  
  697. /* Scan for =-+?%# in string */
  698.  
  699.     if (*s)
  700.     {
  701.     for (cp = s + 1; *cp; cp++)
  702.     {
  703.  
  704. /* Check for end character other than null (=-+?) */
  705.  
  706.         if (any (*cp, "=-+?%#"))
  707.         {
  708.         c = *cp;
  709.  
  710. /* Skip next section in case of % or #.  Check for case of :[=-+?].
  711.  * If found - set flag
  712.  */
  713.         if ((c != '%') && (c != '#') && (*(cp - 1) == ':'))
  714.         {
  715.             colon_f = TRUE;
  716.             *(cp - 1) = 0;
  717.         }
  718.  
  719.         *(cp++) = 0;
  720.         break;
  721.         }
  722.     }
  723.     }
  724.  
  725. /* Cannot have both # & : */
  726.  
  727.     if (hash_f && colon_f)
  728.     {
  729.     ShellErrorMessage (bad_subs);
  730.     ExpansionErrorDetected = TRUE;
  731.     return c;
  732.     }
  733.  
  734. /* Check for * and @ processing */
  735.  
  736.     if (s[1] == 0 && (*s == '*' || *s == '@'))
  737.     {
  738.     if (ParameterCount > 1)
  739.     {
  740.         e.linep = s;
  741.  
  742. /* If hash flag set, convert to string length */
  743.  
  744.         if (hash_f)
  745.         {
  746.         dolp = StringCopy (IntegerToString (ParameterCount - 1));
  747.         PUSHIO (aword, dolp, quoted ? QuotedString_GetNextCharacter
  748.                         : String_GetNextCharacter, null);
  749.         }
  750.  
  751.         else
  752.         {
  753.         PUSHIO (awordlist, ParameterArray + 1,
  754.             SpacedWordList_GetNextCharacter, null);
  755.  
  756.         e.iop->StarAmpersandProcessing =
  757.             (char)(!quoted ? DSA_NULL : ((*s == '*') ? DSA_STAR
  758.                                  : DSA_AMP));
  759.         }
  760.  
  761.         return 0;
  762.     }
  763.  
  764. /* trap the nasty ${=} */
  765.  
  766.     else
  767.     {
  768.         s[0] = '1';
  769.         s[1] = 0;
  770.     }
  771.     }
  772.  
  773. /* Find the current value
  774.  *
  775.  * $~xxx variables are used by the Shell internally and cannot be accessed
  776.  * by the user.
  777.  */
  778.  
  779.     if (*s == '~')
  780.     dolp = null;
  781.  
  782.     else if (!*s || !(isalnum (*s) || any (*s, "#-?$!")))
  783.     {
  784.     dol_special[1] = *s;
  785.     dolp = dol_special;
  786.     }
  787.  
  788.     else if ((dolp = GetVariableAsString (s, TRUE)) == null)
  789.     {
  790.     switch (c)
  791.     {
  792.         case '=':
  793.         if (isdigit (*s))
  794.         {
  795.             ShellErrorMessage ("cannot use ${...=...} with $n");
  796.             ExpansionErrorDetected = TRUE;
  797.             break;
  798.         }
  799.  
  800.         SetVariableFromString (s, cp);
  801.         dolp = GetVariableAsString (s, TRUE);
  802.         break;
  803.  
  804.         case '-':
  805.         dolp = StringCopy (cp);
  806.         break;
  807.  
  808.         case '?':
  809.         if (*cp == 0)
  810.             cp = "parameter null or not set";
  811.  
  812.         ShellErrorMessage (BasicErrorMessage, s, cp);
  813.  
  814.         ExpansionErrorDetected = TRUE;
  815.         break;
  816.     }
  817.     }
  818.  
  819. /* String exists - other processing */
  820.  
  821.     else
  822.     {
  823.     char        *pos;        /* Position for substitute    */
  824.     char        *tsp;
  825.     int        mode;        /* Mode for substitute        */
  826.  
  827.     switch (c)
  828.     {
  829.         case '+':
  830.         dolp = StringCopy (cp);
  831.         break;
  832.  
  833.         case '#':            /* Remove prefix */
  834.         case '%':            /* Remove suffix */
  835.         mode = GM_SHORTEST;
  836.  
  837.         if (*cp == c)
  838.         {
  839.             mode = GM_LONGEST;
  840.             ++cp;
  841.         }
  842.  
  843.         if (c == '#')
  844.         {
  845.             if (GeneralPatternMatch (dolp, cp, FALSE, &pos, mode))
  846.             dolp = StringCopy (pos);
  847.         }
  848.  
  849.         else if (SuffixPatternMatch (dolp, cp, &pos, mode))
  850.         {
  851.             tsp = StringCopy (dolp);
  852.             tsp[pos - dolp] = 0;
  853.             dolp = tsp;
  854.         }
  855.  
  856.         break;
  857.     }
  858.     }
  859.  
  860. /* Check for unset values */
  861.  
  862.     if (FL_TEST ('u') && dolp == null)
  863.     {
  864.     ShellErrorMessage ("unset variable %s\n", s);
  865.     ExpansionErrorDetected = TRUE;
  866.     }
  867.  
  868. /* If hash flag set, convert to string length */
  869.  
  870.     if (hash_f)
  871.     dolp = StringCopy (IntegerToString (strlen (dolp)));
  872.  
  873.     e.linep = s;
  874.     PUSHIO (aword, dolp, quoted ? QuotedString_GetNextCharacter
  875.                 : String_GetNextCharacter, null);
  876.     return 0;
  877. }
  878.  
  879. /*
  880.  * Run the command in `...` and read its output.
  881.  */
  882.  
  883. static bool near ExpandGravedCommand (bool quoted, char End)
  884. {
  885.     char        *cp, *sp;
  886.     int            localpipe, rv;
  887.     jmp_buf        ev, rt;
  888.     C_Op        *outtree;
  889.     Break_C        bc;
  890.     int            (*iof)(IO_State *);
  891.  
  892. /* Save area */
  893.  
  894.     long        s_flags = flags;
  895.     Word_B        *s_wdlist = WordListBlock;
  896.     Word_B        *s_iolist = IOActionBlock;
  897.     Break_C        *S_RList = Return_List;    /* Save loval links    */
  898.     Break_C        *S_BList = Break_List;
  899.     Break_C        *S_SList = SShell_List;
  900.     int            *s_fail = FailReturnPoint;
  901.     bool        s_ProcessingEXECCommand = ProcessingEXECCommand;
  902.     FunctionList    *s_CurrentFunction = CurrentFunction;
  903.     int            Local_depth;
  904.  
  905. /* Check there is an ending grave */
  906.  
  907.     if ((cp = strchr (e.iop->argp->aword, CHAR_BACKQUOTE)) == (char *)NULL)
  908.     {
  909.     ShellErrorMessage ("no closing %c", End);
  910.     return FALSE;
  911.     }
  912.  
  913. /* Create the pipe to read the output from the command string */
  914.  
  915.     if ((localpipe = OpenAPipe ()) < 0)
  916.     {
  917.     ExpansionErrorDetected = TRUE;
  918.     return FALSE;
  919.     }
  920.  
  921. /* Terminate string and initialise save area */
  922.  
  923.     *cp = 0;
  924.  
  925. /* Create a new environment */
  926.  
  927.     S_dup2 (localpipe, 1);
  928.  
  929.     FL_CLEAR ('e');
  930.     FL_CLEAR ('v');
  931.     FL_CLEAR ('n');
  932.  
  933.     sp = StringCopy (e.iop->argp->aword);
  934.     MemoryAreaLevel++;
  935.     unquote (sp, FALSE);
  936.  
  937. /* Set up new environment */
  938.  
  939.     Local_depth = Execute_stack_depth++;
  940.     rv = CreateGlobalVariableList (FLAGS_NONE);
  941.  
  942.     if ((rv != -1) &&
  943.     (CreateNewEnvironment (setjmp (ErrorReturnPoint = ev)) == FALSE))
  944.     {
  945.     Return_List = (Break_C *)NULL;
  946.     Break_List  = (Break_C *)NULL;
  947.     WordListBlock = (Word_B *)NULL;
  948.     IOActionBlock = (Word_B *)NULL;
  949.  
  950.     PUSHIO (aword, sp, Line_GetNextCharacter, null);
  951.     e.cline = GetAllocatedSpace (LINE_MAX);
  952.     e.eline = e.cline + LINE_MAX - 5;
  953.     e.linep = e.cline;
  954.     e.iobase = e.iop;
  955.  
  956. /* Clear interrupt, error, AllowMultipleLines, InParse and execute flags.  */
  957.  
  958.     SW_intr = 0;
  959.     AllowMultipleLines = 0;
  960.     InParser = FALSE;
  961.     ProcessingEXECCommand = TRUE;
  962.     CurrentFunction = (FunctionList *)NULL;
  963.  
  964. /* Parse the line and execute it */
  965.  
  966.     if ((setjmp (FailReturnPoint = rt) == 0) &&
  967.         ((outtree = BuildParseTree ()) != (C_Op *)NULL))
  968.     {
  969.  
  970. /* Check for $(<..) construct */
  971.  
  972.         if (CheckForReDirect (outtree, localpipe))
  973.         rv = 0;
  974.  
  975.         else if (setjmp (bc.CurrentReturnPoint) == 0)
  976.         {
  977.         bc.NextExitLevel = SShell_List;
  978.         SShell_List = &bc;
  979.         rv = ExecuteParseTree (outtree, NOPIPE, NOPIPE, 0);
  980.         }
  981.     }
  982.  
  983. /* Parse error */
  984.  
  985.     else
  986.         rv = -1;
  987.  
  988. /* Clean up any files around we nolonger need */
  989.  
  990.     ClearExtendedLineFile ();
  991.     QuitCurrentEnvironment ();
  992.     }
  993.  
  994.     else
  995.     rv = -1;
  996.  
  997. /* Fail - close pipe and delete it */
  998.  
  999.     if (rv == -1)
  1000.     {
  1001.     S_Delete (localpipe);
  1002.     S_close (localpipe, TRUE);
  1003.     localpipe = -1;
  1004.     }
  1005.  
  1006. /* Restore environment */
  1007.  
  1008.     RestoreEnvironment (0, Local_depth);
  1009.  
  1010. /* Free old space */
  1011.  
  1012.     FreeAllHereFiles (MemoryAreaLevel);
  1013.     ReleaseMemoryArea (MemoryAreaLevel--);    /* free old space */
  1014.  
  1015. /* Ok - completed processing - restore environment and read the pipe */
  1016.  
  1017.     ProcessingEXECCommand = s_ProcessingEXECCommand;
  1018.     flags = s_flags;
  1019.     WordListBlock = s_wdlist;
  1020.     IOActionBlock = s_iolist;
  1021.     FailReturnPoint = s_fail;
  1022.     Return_List = S_RList;
  1023.     Break_List    = S_BList;
  1024.     SShell_List = S_SList;
  1025.     CurrentFunction = s_CurrentFunction;
  1026.  
  1027. /* Move pipe to start so we can read it */
  1028.  
  1029.     *(cp++) = CHAR_BACKQUOTE;
  1030.     e.iop->argp->aword = cp;
  1031.  
  1032.     if (localpipe == -1)
  1033.     {
  1034.     ExpansionErrorDetected = TRUE;
  1035.     return FALSE;
  1036.     }
  1037.  
  1038.     lseek (localpipe, 0L, SEEK_SET);
  1039.     iof = (!quoted) ? NonNLStdOut_GetNextCharacter
  1040.             : ((quoted == MAYBE) ? NonQuoteSO_GetNextCharacter
  1041.                      : StdOut_GetNextCharacter);
  1042.     PUSHIO (afile, ReMapIOHandler (localpipe), iof, null);
  1043.     return TRUE;
  1044. }
  1045.  
  1046. /*
  1047.  * Remove Quotes from a string
  1048.  */
  1049.  
  1050. static char *near unquote (register char *as, bool convert)
  1051. {
  1052.     register char    *s;
  1053.  
  1054. /* Unquote the string */
  1055.  
  1056.     if ((s = as) == (char *)NULL)
  1057.     return as;
  1058.  
  1059.     while (*s)
  1060.     *(s++) &= ~QUOTE;
  1061.  
  1062. /*
  1063.  * Convert from UNIX to DOS format: Slashes to Backslashes in paths and
  1064.  * dash to slash for switches
  1065.  */
  1066.  
  1067.     if (convert)
  1068.     {
  1069.     if (*as == '-')
  1070.         *as = '/';
  1071.  
  1072.     else
  1073.          PATH_TO_DOS (as);
  1074.     }
  1075.  
  1076.     return as;
  1077. }
  1078.  
  1079. /*
  1080.  * Expand *, [] and ?
  1081.  */
  1082.  
  1083. static Word_B * near ExpandGlobbedName (char *cp, Word_B *wb, int mode)
  1084. {
  1085.     int        ReturnValue;
  1086.     void    (*sig_int)(int);        /* Interrupt signal    */
  1087.     glob_t    gp;                /* Globbing structure    */
  1088.  
  1089. /* Ignore null strings */
  1090.  
  1091.     if (cp == (char *)NULL)
  1092.     return wb;
  1093.  
  1094. /* We have to expand the word whilst any words in cp have special characters
  1095.  * in them.  First save the signal handler because I want to process
  1096.  * signals differently in the glob function to allow us to use common code.
  1097.  */
  1098.  
  1099.     GlobbingInterrupted = FALSE;
  1100.     sig_int = signal (SIGINT, _GP_HandleInterrupt);
  1101.  
  1102. /* Expand the words */
  1103.  
  1104.     e.GlobbingFileList = wb;
  1105.     _GP_AddArgument = _GP_ShellAddArgument;
  1106.  
  1107.     ReturnValue = glob (cp, mode, (int (*)())NULL , &gp);
  1108.  
  1109. /* Restore the signals */
  1110.  
  1111.     signal (SIGINT, sig_int);
  1112.  
  1113. /* Check for abort */
  1114.  
  1115.     if (GlobbingInterrupted)
  1116.     raise (SIGINT);
  1117.  
  1118. /* Check for out of memory */
  1119.  
  1120.     if (ReturnValue)
  1121.     PrintErrorMessage (BasicErrorMessage, "sh", Outofmemory1);
  1122.  
  1123. /* Remove the NULL added by glob */
  1124.  
  1125.     (e.GlobbingFileList)->w_nword--;
  1126.     return e.GlobbingFileList;
  1127. }
  1128.  
  1129. /*
  1130.  * Create a new Word Block
  1131.  */
  1132.  
  1133. static Word_B * near CreateNewWordBlock (register int nw)
  1134. {
  1135.     register Word_B    *wb;
  1136.  
  1137.     if ((wb = (Word_B *)
  1138.         GetAllocatedSpace (sizeof (Word_B) + nw * sizeof (char *)))
  1139.         != (Word_B *)NULL)
  1140.     {
  1141.     wb->w_bsize = nw;
  1142.     wb->w_nword = 0;
  1143.     }
  1144.  
  1145.     return wb;
  1146. }
  1147.  
  1148. /*
  1149.  * Add a new word to a Word Block or list
  1150.  */
  1151.  
  1152. Word_B *AddWordToBlock (char *wd, register Word_B *wb)
  1153. {
  1154.     register Word_B    *wb2;
  1155.     register int    nw;
  1156.  
  1157.     if (wb == (Word_B *)NULL)
  1158.     wb = CreateNewWordBlock (NSTART);
  1159.  
  1160.     if (wb == (Word_B *)NULL)
  1161.     return (Word_B *)NULL;
  1162.  
  1163. /* Do we require more space ? */
  1164.  
  1165.     if ((nw = wb->w_nword) >= wb->w_bsize)
  1166.     {
  1167.     if ((wb2 = CreateNewWordBlock (nw * 2)) == (Word_B *)NULL)
  1168.         return (Word_B *)NULL;
  1169.  
  1170.     memcpy ((void *)wb2->w_words, (void *)wb->w_words,
  1171.         nw * sizeof (void *));
  1172.     wb2->w_nword = nw;
  1173.     ReleaseMemoryCell ((void *)wb);
  1174.     wb = wb2;
  1175.     }
  1176.  
  1177. /* Add to the list */
  1178.  
  1179.     wb->w_words[wb->w_nword++] = wd;
  1180.     return wb;
  1181. }
  1182.  
  1183. /*
  1184.  * Convert a word block structure into a array of strings
  1185.  */
  1186.  
  1187. char **GetWordList (register Word_B *wb)
  1188. {
  1189.     register char    **wd;
  1190.     register int    nb;
  1191.  
  1192. /* If the word block is empty or does not exist, return no list */
  1193.  
  1194.     if (wb == (Word_B *)NULL)
  1195.     return (char *)NULL;
  1196.  
  1197. /* Get some space for the array and set it up */
  1198.  
  1199.     if (((nb = sizeof (char *) * wb->w_nword) == 0) ||
  1200.     ((wd = (char **)GetAllocatedSpace (nb)) == (char **)NULL))
  1201.     {
  1202.     ReleaseMemoryCell ((void *)wb);
  1203.     return (char *)NULL;
  1204.     }
  1205.  
  1206.     memcpy ((char *)wd, (char *)wb->w_words, nb);
  1207.     ReleaseMemoryCell ((void *)wb);    /* perhaps should done by caller */
  1208.     return wd;
  1209. }
  1210.  
  1211. /*
  1212.  * Is any character from s1 in s2?  Return a boolean.
  1213.  */
  1214.  
  1215. bool anys (register char *s1, register char *s2)
  1216. {
  1217.     while (*s1)
  1218.     {
  1219.     if (any (*(s1++), s2))
  1220.         return TRUE;
  1221.     }
  1222.  
  1223.     return FALSE;
  1224. }
  1225.  
  1226. /*
  1227.  * Special version of glob.c for the shell.  A bit stripped down, since
  1228.  * neither the shell or stdargv.c use some of the functionality.
  1229.  */
  1230.  
  1231. /* Free up space */
  1232.  
  1233. void globfree (glob_t *gp)
  1234. {
  1235.     int        i = 0;
  1236.  
  1237.     while (i < gp->gl_pathc)
  1238.     free (gp->gl_pathv[i++]);
  1239.  
  1240.     free (gp->gl_pathv);
  1241. }
  1242.  
  1243. /* Main search function */
  1244.  
  1245. int glob (char *Pattern, int flags, int (*ErrorFunction) (char *, int), glob_t *gp)
  1246. {
  1247.     int        ReturnValue;
  1248.     char    *PatternCopy;
  1249.     char    *cp;
  1250.  
  1251. /* If no append mode - initialise */
  1252.  
  1253.     gp->gl_pathc = 0;
  1254.     gp->gl_pathv = (char **)NULL;
  1255.     gp->gl_flags = flags;
  1256.     gp->gl_ef    = ErrorFunction;
  1257.  
  1258.     if ((PatternCopy = alloca (strlen (Pattern) + 1)) == (char *)NULL)
  1259.     return GLOB_NOSPACE;
  1260.  
  1261. /* Expand and kill environment */
  1262.  
  1263.     if (ReturnValue = _GP_ExpandMetaCharacters (strcpy (PatternCopy, Pattern),
  1264.                         gp))
  1265.     return ReturnValue;
  1266.  
  1267. /* Check for no finds.  If add value, strip out \ from the string */
  1268.  
  1269.     if (gp->gl_pathc == 0)
  1270.     {
  1271.     cp = strcpy (PatternCopy, Pattern);
  1272.  
  1273.     while ((cp = strpbrk (cp, "?*[")) != (char *)NULL)
  1274.     {
  1275.         if ((cp == PatternCopy) || (*(cp - 1) != '\\'))
  1276.         cp++;
  1277.  
  1278.         else
  1279.         memmove (cp - 1, cp, strlen (cp) + 1);
  1280.     }
  1281.  
  1282.     if (ReturnValue = (*_GP_AddArgument) (PatternCopy, gp))
  1283.         return ReturnValue;
  1284.     }
  1285.  
  1286. /* Terminate string */
  1287.  
  1288.     if ((gp->gl_pathc != 0) &&
  1289.     (ReturnValue = (*_GP_AddArgument) ((char *)NULL, gp)))
  1290.     return ReturnValue;
  1291.  
  1292. /* Get the sort length */
  1293.  
  1294.     if (gp->gl_pathc > 1)
  1295.     qsort (&gp->gl_pathv[0], gp->gl_pathc, sizeof (char *), _GP_SortCompare);
  1296.  
  1297.     return 0;
  1298. }
  1299.  
  1300. /* Compare function for sort */
  1301.  
  1302. int _GP_SortCompare (char **a1, char **a2)
  1303. {
  1304.     return strcmp (*a1, *a2);
  1305. }
  1306.  
  1307. /* Expand a field if it has metacharacters in it */
  1308.  
  1309. static int near _GP_ExpandField (char *CurrentDirectoryPattern,
  1310.                  char *AppendString, glob_t *gp)
  1311. {
  1312.     int         i;
  1313.     int            ReturnValue = 0;    /* Return Value        */
  1314.     char        *FullFileName;        /* Search file name    */
  1315.     char        *FileNameStart;
  1316.     char        *MatchString;        /* Match string        */
  1317.     DIR            *DirHandler;
  1318.     struct dirent    *CurrentDirectoryEntry;
  1319.     unsigned int    CurrentDrive;        /* Current drive    */
  1320.     unsigned int    MaxDrives;        /* Max drive        */
  1321.     unsigned int    SelectedDrive;        /* Selected drive    */
  1322.     unsigned int    y_drive;        /* Dummies        */
  1323.     char        *DriveCharacter;    /* Multi-drive flag    */
  1324.     char        SDriveString[2];
  1325.     bool        IgnoreCase = TRUE;
  1326.  
  1327. /* Globbing interrupted ? */
  1328.  
  1329.     if (GlobbingInterrupted)
  1330.     return GLOB_ABEND;
  1331.  
  1332. /* Convert file name to lower case */
  1333.  
  1334. #ifndef OS2
  1335.     strlwr (CurrentDirectoryPattern);
  1336. #else
  1337.     if (!IsHPFSFileSystem (CurrentDirectoryPattern))
  1338.     strlwr (CurrentDirectoryPattern);
  1339.  
  1340.     else
  1341.     IgnoreCase = FALSE;
  1342. #endif
  1343.  
  1344. /* Search all drives ? */
  1345.  
  1346.     if ((DriveCharacter = _GP_CheckForMultipleDrives (CurrentDirectoryPattern))
  1347.         != (char *)NULL)
  1348.     {
  1349.     CurrentDrive = GetCurrentDrive ();
  1350.     MaxDrives = SetCurrentDrive (CurrentDrive);
  1351.     SDriveString[1] = 0;
  1352.  
  1353.     for (SelectedDrive = 1; SelectedDrive <= MaxDrives; ++SelectedDrive)
  1354.     {
  1355.         SetCurrentDrive (SelectedDrive);
  1356.         y_drive = GetCurrentDrive ();
  1357.         SetCurrentDrive (CurrentDrive);
  1358.  
  1359. /* Check to see if the second diskette drive is really there */
  1360.  
  1361.         if ((_GP_GetNumberofFloppyDrives () < 2) && (SelectedDrive == 2))
  1362.         continue;
  1363.  
  1364. /* If the drive exists and is in our list - process it */
  1365.  
  1366.         *DriveCharacter = 0;
  1367.         *SDriveString = (char)(SelectedDrive + 'a' - 1);
  1368.         strlwr (CurrentDirectoryPattern);
  1369.  
  1370.         if ((y_drive == SelectedDrive) &&
  1371.         GeneralPatternMatch (SDriveString, CurrentDirectoryPattern,
  1372.                      TRUE, (char **)NULL, GM_ALL))
  1373.         {
  1374.         if ((FullFileName = alloca (strlen (DriveCharacter) + 3))
  1375.                 == (char *)NULL)
  1376.             return GLOB_NOSPACE;
  1377.  
  1378.         *DriveCharacter = ':';
  1379.         *FullFileName = *SDriveString;
  1380.         strcpy (FullFileName + 1, DriveCharacter);
  1381.  
  1382.         if (i = _GP_ExpandField (FullFileName, AppendString, gp))
  1383.             return i;
  1384.         }
  1385.  
  1386.         *DriveCharacter = ':';
  1387.     }
  1388.  
  1389.     return 0;
  1390.     }
  1391.  
  1392. /* Get the path length */
  1393.  
  1394.  
  1395.     if (((MatchString = strrchr (CurrentDirectoryPattern,
  1396.                  CHAR_UNIX_DIRECTORY)) == (char *)NULL)
  1397.     && (*(CurrentDirectoryPattern + 1) == ':'))
  1398.     MatchString = CurrentDirectoryPattern + 1;
  1399.  
  1400. /* Set up file name for search */
  1401.  
  1402.     if ((MatchString == (char *)NULL) || (*MatchString == ':'))
  1403.     {
  1404.     if ((FullFileName = alloca (NAME_MAX + 7 +
  1405.                     strlen (AppendString))) == (char *)NULL)
  1406.         return GLOB_NOSPACE;
  1407.  
  1408.     if (MatchString != (char *)NULL)
  1409.         *(strcpy (FullFileName, "x:.")) = *CurrentDirectoryPattern;
  1410.  
  1411.     else
  1412.         strcpy (FullFileName, ".");
  1413.  
  1414.     FileNameStart = FullFileName +
  1415.             (int)((MatchString != (char *)NULL) ? 2 : 0);
  1416.     }
  1417.  
  1418. /* Case of /<directory>/... */
  1419.  
  1420.     else if ((FullFileName = alloca (NAME_MAX + 4 + strlen (AppendString) +
  1421.                 (i = (int)(MatchString - CurrentDirectoryPattern))))
  1422.             == (char *)NULL)
  1423.         return GLOB_NOSPACE;
  1424.  
  1425.     else
  1426.     {
  1427.     strncpy (FullFileName, CurrentDirectoryPattern, i);
  1428.     *((FileNameStart = FullFileName + i)) = 0;
  1429.     strcpy (FileNameStart++, DirectorySeparator);
  1430.     }
  1431.  
  1432.     MatchString = (MatchString == (char *)NULL) ? CurrentDirectoryPattern
  1433.                         : MatchString + 1;
  1434.  
  1435. /* Search for file names */
  1436.  
  1437.     if ((DirHandler = opendir (CheckDOSFileName (FullFileName))) == (DIR *)NULL)
  1438.     return 0;
  1439.  
  1440. /* Are there any matches */
  1441.  
  1442.     while ((CurrentDirectoryEntry = readdir (DirHandler)) !=
  1443.         (struct dirent *)NULL)
  1444.     {
  1445.  
  1446. /* Globbing interrupted ? */
  1447.  
  1448.     if (GlobbingInterrupted)
  1449.     {
  1450.         ReturnValue = GLOB_ABEND;
  1451.         break;
  1452.     }
  1453.  
  1454. /* Ignore . files? */
  1455.  
  1456.     if ((*CurrentDirectoryEntry->d_name == '.') && (*MatchString != '.'))
  1457.         continue;
  1458.  
  1459. /* Check for match */
  1460.  
  1461.     if (GeneralPatternMatch (CurrentDirectoryEntry->d_name, MatchString,
  1462.                  IgnoreCase, (char **)NULL, GM_ALL))
  1463.     {
  1464.         strcpy (FileNameStart, CurrentDirectoryEntry->d_name);
  1465.  
  1466. /* If the postfix is not null, this must be a directory */
  1467.  
  1468.         if (strlen (AppendString))
  1469.         {
  1470.         char            *p;
  1471.  
  1472. /* If not a directory - go to the next file */
  1473.  
  1474.  
  1475.         if (!IsDirectory (FullFileName))
  1476.             continue;
  1477.  
  1478. /* Are there any metacharacters in the postfix? */
  1479.  
  1480.         if ((p = strpbrk (AppendString, _GP_MetaChars)) == (char *)NULL)
  1481.         {
  1482.  
  1483. /* No - build the file name and check it exists */
  1484.  
  1485.             strcat (strcat (FileNameStart, DirectorySeparator),
  1486.                 AppendString);
  1487.  
  1488.             if ((access (CheckDOSFileName (FullFileName), F_OK) == 0) &&
  1489.             (ReturnValue = (*_GP_AddArgument) (FullFileName, gp)))
  1490.             break;
  1491.         }
  1492.  
  1493. /* Yes - build the filename upto the start of the meta characters */
  1494.  
  1495.         else
  1496.         {
  1497.             if ((p = strchr (p, CHAR_UNIX_DIRECTORY)) != (char *)NULL)
  1498.             *(p++) = 0;
  1499.  
  1500.             else
  1501.             p = null;
  1502.  
  1503. /* Build the new directory name and check it out */
  1504.  
  1505.             strcat (strcat (FileNameStart, DirectorySeparator),
  1506.                 AppendString);
  1507.             ReturnValue = _GP_ExpandField (FullFileName, p, gp);
  1508.  
  1509.             if (p != null)
  1510.                *(--p) = CHAR_UNIX_DIRECTORY;
  1511.  
  1512. /* Check for errors */
  1513.  
  1514.             if (ReturnValue)
  1515.             break;
  1516.         }
  1517.         }
  1518.  
  1519. /* Process this file.  If error - terminate */
  1520.  
  1521.         else if ((access (CheckDOSFileName (FullFileName), F_OK) == 0) &&
  1522.              (ReturnValue = (*_GP_AddArgument) (FullFileName, gp)))
  1523.         break;
  1524.     }
  1525.     }
  1526.  
  1527.     closedir (DirHandler);
  1528.     return ReturnValue;
  1529. }
  1530.  
  1531. /* Find the location of meta-characters.  If no meta, add the argument and
  1532.  * return.  If meta characters, expand directory containing meta characters.
  1533.  */
  1534.  
  1535. static int near _GP_ExpandMetaCharacters (char *file, glob_t *gp)
  1536. {
  1537.     char    *p;
  1538.     int        ReturnValue;
  1539.  
  1540. /* Globbing interrupted ? */
  1541.  
  1542.     if (GlobbingInterrupted)
  1543.     return GLOB_ABEND;
  1544.  
  1545. /* No metas - add to string */
  1546.  
  1547.     if ((p = strpbrk (file, _GP_MetaChars)) == (char *)NULL)
  1548.     {
  1549.     if (access (CheckDOSFileName (file), F_OK) < 0)
  1550.         return 0;
  1551.  
  1552.     return (*_GP_AddArgument) (file, gp);
  1553.     }
  1554.  
  1555. /* Ok - metas, find the end of the start of the directory */
  1556.  
  1557.     else if ((p = strchr (p, CHAR_UNIX_DIRECTORY)) != (char *)NULL)
  1558.     *(p++) = 0;
  1559.  
  1560.     else
  1561.     p = null;
  1562.  
  1563. /* Continue recusive match */
  1564.  
  1565.     ReturnValue = _GP_ExpandField (file, p, gp);
  1566.  
  1567. /* Restore if necessary */
  1568.  
  1569.     if (p != null)
  1570.        *(--p) = CHAR_UNIX_DIRECTORY;
  1571.  
  1572.     return ReturnValue;
  1573. }
  1574.  
  1575. /* Check for multi_drive prefix */
  1576.  
  1577. static char * near _GP_CheckForMultipleDrives (char *prefix)
  1578. {
  1579.     if (strlen (prefix) < 2)
  1580.     return (char *)NULL;
  1581.  
  1582.     if (((*prefix == '*') || (*prefix == '?')) && (prefix[1] == ':'))
  1583.     return prefix + 1;
  1584.  
  1585.     if (*prefix != CHAR_OPEN_BRACKETS)
  1586.     return (char *)NULL;
  1587.  
  1588.     while (*prefix && (*prefix != CHAR_CLOSE_BRACKETS))
  1589.     {
  1590.     if ((*prefix == '\\') && (*(prefix + 1)))
  1591.         ++prefix;
  1592.  
  1593.     ++prefix;
  1594.     }
  1595.  
  1596.     return (*prefix && (*(prefix + 1) == ':')) ? prefix + 1 : (char *)NULL;
  1597. }
  1598.  
  1599. /*
  1600.  * Stdargv version of AddArgument
  1601.  *
  1602.  * Add an argument to the stack - file is assumed to be a array big enough
  1603.  * for the file name + 2
  1604.  */
  1605.  
  1606. static int near _GP_StdargvAddArgument (char *file, glob_t *gp)
  1607. {
  1608.     size_t    Offset = (gp->gl_pathc + 50) * sizeof (char *);
  1609.  
  1610. /* Malloc space if necessary */
  1611.  
  1612.     if (gp->gl_pathc == 0)
  1613.     gp->gl_pathv = (char **)malloc (Offset);
  1614.  
  1615.     else if ((gp->gl_pathc % 50) == 0)
  1616.     gp->gl_pathv = (char **)realloc (gp->gl_pathv, Offset);
  1617.  
  1618.     if (gp->gl_pathv == (char **)NULL)
  1619.     return GLOB_NOSPACE;
  1620.  
  1621. /* OK got space, check for End of list ? */
  1622.  
  1623.     if (file == (char *)NULL)
  1624.     gp->gl_pathv[gp->gl_pathc] = (char *)NULL;
  1625.  
  1626.     else if ((gp->gl_pathv[gp->gl_pathc++] = strdup (file)) == (char *)NULL)
  1627.     return GLOB_NOSPACE;
  1628.  
  1629.     return 0;
  1630. }
  1631.  
  1632. /*
  1633.  * Shell version of AddArgument
  1634.  *
  1635.  * Add an argument to the stack - file is assumed to be a array big enough
  1636.  * for the file name + 2
  1637.  */
  1638.  
  1639. static int near _GP_ShellAddArgument (char *file, glob_t *gp)
  1640. {
  1641.     Word_B    *wb2, *wb;
  1642.     char    *CopyFN = (char *)NULL;
  1643.     int        nw;
  1644.     static int    StartOffset = 0;
  1645.  
  1646.     if (GlobbingInterrupted)
  1647.     return GLOB_ABEND;
  1648.  
  1649.     if ((wb = e.GlobbingFileList) == (Word_B *)NULL)
  1650.     wb = CreateNewWordBlock (NSTART);
  1651.  
  1652.     if (wb == (Word_B *)NULL)
  1653.     return GLOB_NOSPACE;
  1654.  
  1655. /* Do we require more space ? */
  1656.  
  1657.     if ((nw = wb->w_nword) >= wb->w_bsize)
  1658.     {
  1659.     if ((wb2 = CreateNewWordBlock (nw * 2)) == (Word_B *)NULL)
  1660.         return GLOB_NOSPACE;
  1661.  
  1662.     memcpy ((char *)wb2->w_words, (char *)wb->w_words, nw*sizeof(char *));
  1663.     wb2->w_nword = nw;
  1664.     ReleaseMemoryCell ((void *)wb);
  1665.     wb = wb2;
  1666.     }
  1667.  
  1668. /* If the first time through for this expansion, save the start of
  1669.  * expansion address
  1670.  */
  1671.  
  1672.     if (!gp->gl_pathc)
  1673.     StartOffset = nw;
  1674.  
  1675. /* Add to the list */
  1676.  
  1677.     if (file != (char *)NULL)
  1678.     {
  1679.     if ((CopyFN = AllocateMemoryCell (strlen (file) + 2)) == (char *)NULL)
  1680.         return GLOB_NOSPACE;
  1681.  
  1682.     strcpy (CopyFN, file);
  1683.  
  1684. /* Mark directory entries? */
  1685.  
  1686.     if ((GlobalFlags & FLAGS_MARKDIRECTORY) &&
  1687.         IsDirectory (CopyFN))
  1688.         strcat (CopyFN, "/");
  1689.  
  1690.     ++(gp->gl_pathc);
  1691.     }
  1692.  
  1693.     unquote (CopyFN, (bool)(gp->gl_flags & GLOB_CONVERT));
  1694.     (e.GlobbingFileList = wb)->w_words[wb->w_nword++] = CopyFN;
  1695.     gp->gl_pathv = &wb->w_words[StartOffset];
  1696.     return 0;
  1697. }
  1698.  
  1699. /*
  1700.  * Handle interrupt signals during globbing
  1701.  */
  1702.  
  1703. static void _GP_HandleInterrupt (int signo)
  1704. {
  1705.     signal (signo, _GP_HandleInterrupt);
  1706.     GlobbingInterrupted = TRUE;
  1707. }
  1708.  
  1709. /* Return the number of floppy disks */
  1710.  
  1711. static int near    _GP_GetNumberofFloppyDrives (void)
  1712. {
  1713. #ifdef OS2
  1714.     BYTE    nflop = 1;
  1715.     DosDevConfig (&nflop, DEVINFO_FLOPPY, 0);
  1716.     return nflop;
  1717. #else
  1718.     return ((_bios_equiplist () & 0x00c0) >> 6) + 1;
  1719. #endif
  1720. }
  1721.  
  1722. /*
  1723.  * Build up the parameter variables
  1724.  */
  1725.  
  1726. Word_B *AddParameter (char *value, Word_B *wb, char *function)
  1727. {
  1728.     char    **NewArray;
  1729.     int        Count;
  1730.     int        i;
  1731.  
  1732. /* Add to block */
  1733.  
  1734.     if ((wb = AddWordToBlock (value, wb)) == (Word_B *)NULL)
  1735.     {
  1736.     fprintf (stderr, BasicErrorMessage, function, Outofmemory1);
  1737.     return (Word_B *)NULL;
  1738.     }
  1739.  
  1740. /* If not end, return */
  1741.  
  1742.     if (value != (char *)NULL)
  1743.     return wb;
  1744.  
  1745. /* Get number of entries */
  1746.  
  1747.     Count = wb->w_nword - 1;
  1748.  
  1749. /* Convert to array */
  1750.  
  1751.     if ((NewArray = GetWordList (wb)) == (char **)NULL)
  1752.     {
  1753.     fprintf (stderr, BasicErrorMessage, function, Outofmemory1);
  1754.     return (Word_B *)NULL;
  1755.     }
  1756.  
  1757. /* Release old array.  Note: never release entry zero */
  1758.  
  1759.     if (ParameterArray != (char **)NULL)
  1760.     {
  1761.     for (i = 1; i < ParameterCount; ++i)
  1762.         ReleaseMemoryCell ((void *)ParameterArray [i]);
  1763.  
  1764.     ReleaseMemoryCell ((void *)ParameterArray);
  1765.     }
  1766.  
  1767. /* Set new array to no-release */
  1768.  
  1769.     for (i = 0; i < Count; ++i)
  1770.     SetMemoryAreaNumber ((void *)NewArray[i], 0);
  1771.  
  1772.     SetMemoryAreaNumber ((void *)NewArray, 0);
  1773.  
  1774. /* Reset globals and environment */
  1775.  
  1776.     ParameterCount = Count - 1;
  1777.     ParameterArray = NewArray;
  1778.     SetVariableFromNumeric (ParameterCountVariable, (long)ParameterCount);
  1779.     return wb;
  1780. }
  1781.  
  1782. /*
  1783.  * Check for assignment X=Y
  1784.  */
  1785.  
  1786. bool    IsVariableAssignment (register char *s)
  1787. {
  1788.     return (IsValidVariableName (s) == '=') ? TRUE : FALSE;
  1789. }
  1790.  
  1791. /*
  1792.  * Is this a valid variable name
  1793.  */
  1794.  
  1795. char    IsValidVariableName (register char *s)
  1796. {
  1797.     if (!isalpha (*s))
  1798.     return *s;
  1799.  
  1800.     while (*s && isalnum (*s))
  1801.     ++s;
  1802.  
  1803.     return *s;
  1804. }
  1805.  
  1806. /*
  1807.  * Check for $(<...) construct
  1808.  */
  1809.  
  1810. static bool near CheckForReDirect (C_Op *t, int fp_pipe)
  1811. {
  1812.     IO_Actions        **iopp = t->ioact;
  1813.  
  1814.     if ((t->type != TCOM) || (*t->words != (char *)NULL) ||
  1815.     (iopp == (IO_Actions **)NULL))
  1816.     return FALSE;
  1817.  
  1818. /* Look for the stdin selection */
  1819.  
  1820.     while (*iopp != (IO_Actions *)NULL)
  1821.     {
  1822.     if (((*iopp)->io_unit == IODEFAULT) &&
  1823.         ((*iopp)->io_flag & (IOREAD | IOHERE)))
  1824.     {
  1825.         (*iopp)->io_unit = fp_pipe;
  1826.         SetUpIOHandlers (*iopp, -1, -1);
  1827.         return TRUE;
  1828.     }
  1829.  
  1830.     iopp++;
  1831.     }
  1832.  
  1833.     return FALSE;
  1834. }
  1835.